/*
 * Copyright (c) 2025 EasyAdmin
 * All rights reserved.
 *
 * This file is part of the [EasAdmin] project.
 * Unauthorized copying of this file, via any medium, is strictly prohibited.
 * Proprietary and confidential.
 *
 * Author: EasyAdmin ( 1073602@qq.com )
 * Website: https://www.xhxiao.com
 */
package com.easy.module.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easy.framework.common.constant.Constants;
import com.easy.framework.common.enums.CommonStatus;
import com.easy.framework.common.model.TreeModel;
import com.easy.framework.common.utils.Func;
import com.easy.framework.common.utils.StringUtil;
import com.easy.framework.security.utils.SecurityUtils;
import com.easy.module.system.entity.SysMenu;
import com.easy.module.system.entity.SysRoleMenu;
import com.easy.module.system.enums.MenuType;
import com.easy.module.system.mapper.SysMenuMapper;
import com.easy.module.system.mapper.SysRoleMenuMapper;
import com.easy.module.system.query.SysMenuQuery;
import com.easy.module.system.service.ISysMenuService;
import com.easy.module.system.service.ISysRoleService;
import com.easy.module.system.vo.MetaVO;
import com.easy.module.system.vo.RouterVO;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 菜单权限表 Service业务
 *
 * @author EasyAdmin ( 1073602@qq.com )
 * @date 2025-08-19
 */
@RequiredArgsConstructor
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {

    private final ISysRoleService roleService;

    private final SysRoleMenuMapper roleMenuMapper;
    private final SysRoleMenuMapper sysRoleMenuMapper;

    /**
     * 获取查询对象
     *
     * @param query
     * @return
     */
    @Override
    public LambdaQueryWrapper<SysMenu> lambdaQuery(SysMenuQuery query) {
        return lambdaQueryOptional(query).orElse(lambdaQueryWrapper()).orderByAsc(SysMenu::getParentId).orderByAsc(SysMenu::getSort);
    }

    @Override
    public List<SysMenu> listByUserId(SysMenuQuery query, Long userId) {
        List<SysMenu> menuList = null;
        // 管理员显示所有菜单信息
        if (SecurityUtils.isAdmin(userId)) {
            menuList = list(query);
        } else {
            menuList = getUserMenuList(query, userId);
        }
        return menuList;
    }

    /**
     * 新增菜单权限
     *
     * @param dto 菜单权限
     * @return 结果
     */
    @Override
    public boolean add(SysMenu dto) {
        return save(dto);
    }

    /**
     * 修改菜单权限
     *
     * @param dto 菜单权限
     * @return 结果
     */
    @Override
    public boolean update(SysMenu dto) {
        return updateById(dto);
    }

    /**
     * 删除菜单权限信息
     *
     * @param menuId 菜单权限主键
     * @return 结果
     */
    @Override
    public boolean deleteById(Long menuId) {
        return removeById(menuId);
    }


    /**
     * 获取用户权限
     *
     * @param userId  用户ID
     * @param roleIds 角色编号列表
     * @return 菜单权限信息
     */
    @Cacheable(value = Constants.CACHE + "oauth2:authorities", key = "#userId", condition = "#userId != null")
    @Override
    public Set<String> getUserAuthorities(@NonNull Long userId, List<Long> roleIds) {
        Set<String> permissions = new HashSet<String>();
        // 管理员拥有所有权限
        if (SecurityUtils.isAdmin(userId)) {
            permissions.add(Constants.ALL_PERMISSION);
        } else {
            if (Func.isNotEmpty(roleIds)) {
                for (Long roleId : roleIds) {
                    List<SysMenu> menuList = list(Wrappers.<SysMenu>lambdaQuery()
                            .eq(SysMenu::getStatus, CommonStatus.enabled.getValue())
                            .isNotNull(SysMenu::getSign)
                            .apply(StringUtil.format(" (sys_menu.id in (select distinct rm.menu_id from sys_role_menu as rm where rm.role_id = {}))", roleId))
                    );
                    Optional.ofNullable(menuList).ifPresent(o -> {
                        permissions.addAll(o.stream().filter(m -> Func.isNotEmpty(m.getSign())).map(SysMenu::getSign).collect(Collectors.toSet()));
                    });
                }
            }
        }
        return permissions;
    }

    @Override
    public List<SysMenu> getUserMenuList(SysMenuQuery query, Long userId) {
        LambdaQueryWrapper<SysMenu> wrapper = lambdaQuery(query);
        if (!SecurityUtils.isAdmin(userId)) {
            wrapper.apply(StringUtil.format(" (sys_menu.id in (select distinct rm.menu_id from sys_role_menu rm join sys_user_role ur on rm.role_id= ur.role_id where ur.user_id = {}))", userId));
        }
        return list(wrapper);
    }


    @Override
    public List<SysMenu> getUserMenuTree(Long userId) {
        SysMenuQuery query = new SysMenuQuery();
        query.setStatus(CommonStatus.enabled.getValue());
        query.setInTypes(new String[]{
                MenuType.BLOCK.getCode(),
                MenuType.DIR.getCode(),
                MenuType.MENU.getCode(),
                MenuType.XCODE.getCode()
        });
        List<SysMenu> menuList = getUserMenuList(query, userId);
        return getMenuTree(menuList, Constants.TREE_ROOT_ID);
    }

    /**
     * 递归构建树形菜单
     *
     * @param list
     * @param parentId
     * @return
     */
    private List<SysMenu> getMenuTree(List<SysMenu> list, Long parentId) {
        List<SysMenu> menuList = new ArrayList<>();
        if (Func.isNotEmpty(list)) {
            for (Iterator<SysMenu> iterator = list.iterator(); iterator.hasNext(); ) {
                SysMenu menu = iterator.next();
                if (parentId.equals(menu.getParentId())) {
                    menu.setChildren(getMenuTree(list, menu.getId()));
                    menu.setHasChildren(Func.isNotEmpty(menu.getChildren()));
                    menuList.add(menu);
                }
            }
        }
        return menuList;
    }


    @Cacheable(value = Constants.CACHE + "oauth2:routers", key = "#userId", condition = "#userId != null")
    @Override
    public List<RouterVO> getRouters(Long userId) {
        return getRouters(getUserMenuTree(userId));
    }

    @Override
    public List<RouterVO> getRouters(List<SysMenu> menus) {
        List<RouterVO> routers = new LinkedList<RouterVO>();
        if (Func.isNotEmpty(menus)) {
            for (SysMenu menu : menus) {
                RouterVO router = new RouterVO();
                router.setPath(getRouterPath(menu));
                router.setQuery(menu.getQuery());
                router.setMeta(new MetaVO(menu));
                List<RouterVO> childList = getRouters(menu.getChildren());
                if(Func.isNotEmpty(childList) && Func.isNotEmpty(menu.getRedirect()) && menu.getRedirect()){
                    router.setRedirect(menu.getPath().startsWith("/") ? menu.getComponent() : "/"+menu.getPath());
                } else {
                    router.setComponent(getComponent(menu));
                }
                router.setChildren(childList);
                routers.add(router);
            }
        }
        return routers;
    }

    /**
     * 是否为菜单内部跳转
     *
     * @param menu 菜单信息
     * @return 结果
     */
    public boolean isMenuFrame(SysMenu menu) {
        return menu.getParentId().equals(0L) && MenuType.MENU.getCode().equals(menu.getType()) && !menu.getIsFrame();
    }

    /**
     * 获取路由地址
     *
     * @param menu 菜单信息
     * @return 路由地址
     */
    public String getRouterPath(SysMenu menu) {
        String routerPath = menu.getPath();
//        if (!menu.getParentId().equals(0L) && isInnerLink(menu)) {
//            routerPath = StringUtils.replaceEach(routerPath,
//                    new String[]{Constants.HTTP, Constants.HTTPS, Constants.WWW, "."},
//                    new String[]{"", "", "", "/"});
//        }
        if (MenuType.DIR.getCode().equals(menu.getType()) && !menu.getIsFrame()) {
            routerPath = "/" + menu.getPath();
        }
        // 非外链并且是一级目录（类型为菜单）
        else if (isMenuFrame(menu)) {
            routerPath = "/";
        }
        return routerPath;
    }

    /**
     * 是否为内链组件
     *
     * @param menu 菜单信息
     * @return 结果
     */
    public boolean isInnerLink(SysMenu menu) {
        return !menu.getIsFrame() && StringUtil.ishttp(menu.getPath());
    }

    /**
     * 获取组件信息
     *
     * @param menu 菜单信息
     * @return 组件信息
     */
    public String getComponent(SysMenu menu) {
        String component = Constants.LAYOUT;
        if (Func.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) {
            component = menu.getComponent();
        } else if (Func.isEmpty(menu.getComponent()) && !menu.getParentId().equals(0L) && isInnerLink(menu)) {
            component = Constants.INNER_LINK;
        }
        return component;
    }

    /**
     * 是否存在菜单子节点
     *
     * @param menuId 菜单ID
     * @return 结果
     */
    @Override
    public boolean hasChildByMenuId(Long menuId) {
        return count(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getParentId, menuId)) > 0;
    }

    /**
     * 查询菜单是否存在角色
     *
     * @param menuId 菜单ID
     * @return 结果
     */
    @Override
    public boolean hasRoleByMenuId(Long menuId) {
        return roleMenuMapper.selectCount(Wrappers.<SysRoleMenu>lambdaQuery().eq(SysRoleMenu::getMenuId, menuId)) > 0L;
    }

    @Override
    public List<SysMenu> children(SysMenuQuery query, Long userId) {
        Long parentId = query.getParentId();
        query.setParentId(null);
        List<SysMenu> list = listByUserId(query, userId);
        List<SysMenu> menuList = null;
        if (Func.isNotEmpty(parentId)) {
            menuList = list.stream().filter(m -> parentId.equals(m.getParentId())).collect(Collectors.toList());
            if (Func.isNotEmpty(menuList)) {
                menuList.forEach(m -> {
                    m.setHasChildren(list.stream().filter(w -> w.getParentId().equals(m.getId())).count() > 0);
                });
            }
        } else {
            menuList = list;
        }
        return menuList;
    }


    @Override
    public List<TreeModel> treeList(List<SysMenu> list) {
        List<TreeModel> treeModelList = new ArrayList<>();
        if (Func.isNotEmpty(list)) {
            List<Long> tempList = list.stream().map(SysMenu::getId).collect(Collectors.toList());
            for (SysMenu menu : list) {
                // 如果是顶级节点, 遍历该父节点的所有子节点
                if (!tempList.contains(menu.getParentId())) {
                    treeModelList.add(new TreeModel(menu.getId(), menu.getName(), childMenu(list, menu.getId())));
                }
            }
        }
        return treeModelList;
    }

    private List<TreeModel> childMenu(List<SysMenu> menuList, Long parentId) {
        List<TreeModel> childList = new ArrayList<TreeModel>();
        List<SysMenu> tempList = menuList.stream().filter(d -> parentId.equals(d.getParentId())).collect(Collectors.toList());
        if (Func.isNotEmpty(tempList)) {
            for (SysMenu menu : tempList) {
                childList.add(new TreeModel(menu.getId(), menu.getName(), childMenu(menuList, menu.getId())));
            }
        }
        return childList;
    }

    @Override
    public List<Long> getMenuIdListByRoleId(Long roleId) {
        return sysRoleMenuMapper.getMenuIdListByRoleId(roleId);
    }
}
